#include "response.h"
#include <arpa/inet.h>
#include <sys/sendfile.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

const char* server_name = "liso/1.0";
const char* http_version = "HTTP/1.1";
const char* default_index_file = "index.html";
const char* static_site_path = "./static_site";

void handle_request(int client_sock, ssize_t readret, char* recv_buf){
    // 解析request
    Request *request = parse(recv_buf, readret, client_sock);

    // 错误的请求
    if(request == NULL){
        response400(client_sock);
        return;
    }
    
    // 不支持的HTTP版本
    if(strcmp(http_version, request->http_version)){
        response505(client_sock);
    }

    else if(!strcmp(request->http_method, "GET")){
        response_get(client_sock, request);
    }
    else if(!strcmp(request->http_method, "HEAD")){
        response_head(client_sock, request);
    }
    else if(!strcmp(request->http_method, "POST")){
        response_echo(client_sock, recv_buf);
    }
    else{
        response501(client_sock);
    }

    free(request->headers);
    free(request);
}

void response_echo(int client_sock, char* recv_buf){
    Send_nbytes(client_sock, recv_buf, strlen(recv_buf));
}

int response_get(int client_sock, Request* request){
    char response[4096];
    char fullpath[1024];
    char extension[16];
    char mime_type[64];
    char curr_time[256];
    char last_modified[256];
    size_t content_length;
    char content_len_str[16];
    char connection_header_val[32];

    // fullpath: 找到静态网页
    strcpy(fullpath, static_site_path);     // ./static_site
    strcat(fullpath, request->http_uri);    // /
    if(is_dir(fullpath)){
        strcat(fullpath, default_index_file);
    }
    
    // 如果没有那个文件就返回404
    if (access(fullpath, F_OK) < 0){        // F_OK 判断文件是否存在
        // dump_log("[response] request file not found\n");
        response404(client_sock, response);
        return 0;
    }

    // extensio mime_type: 得到content type
    get_extension(fullpath, extension);
    get_mime_type(extension, mime_type);

    // curr_time: 得到当前时间
    get_curr_time(curr_time, 256);

    // last_modified: 得到最后修改时间
    get_flmodified(fullpath, last_modified, 256);

    // content_length: 得到 content length
    content_length = get_file_len(fullpath);
    sprintf(content_len_str, "%zu", content_length);    // %zu用于输出size_t类型

    // 构造 response
    strcat(response, http_version);
    strcat(response, " 200 OK\r\n");

    strcat(response, "Server: ");
    strcat(response, server_name);
    strcat(response, "\r\n");

    strcat(response, "Date: ");
    strcat(response, curr_time);
    strcat(response, "\r\n");

    strcat(response, "Content-Length: ");
    strcat(response, content_len_str);
    strcat(response, "\r\n");

    strcat(response, "Content-type: ");
    strcat(response, mime_type);
    strcat(response, "\r\n");

    strcat(response, "Last-modified: ");
    strcat(response, last_modified);
    strcat(response, "\r\n");

    // connection_header_val: 根据request中的connect的类型 (close | keep-alive) 设置response的connection类型
    memset(connection_header_val, 0, sizeof(connection_header_val));
    get_header_value(request, "Connection", connection_header_val);
    if (!strcmp(connection_header_val, "close")){
        strcat(response, "Connection: close\r\n");
    }
    else{
        strcat(response, "Connection: keep-alive\r\n");
    }
    strcat(response, "\r\n");

    // printf("[response] response = \n%s\n", response);
    // printf("[response] fullpath = %s\n", fullpath);
    Send_nbytes(client_sock, response, strlen(response));
    send_file_to_client(client_sock, fullpath);
    return 0;
}

int response_head(int client_sock, Request* request){
    char response[4096];
    char fullpath[1024];
    char extension[16];
    char mime_type[64];
    char curr_time[256];
    char last_modified[256];
    size_t content_length;
    char content_len_str[16];
    char connection_header_val[32];

    // fullpath: 找到静态网页
    strcpy(fullpath, static_site_path);     // ./static_site
    strcat(fullpath, request->http_uri);    // /
    if(is_dir(fullpath)){
        strcat(fullpath, default_index_file);
    }
    
    // 如果没有那个文件就返回404
    if (access(fullpath, F_OK) < 0){        // F_OK 判断文件是否存在
        // dump_log("[response] request file not found\n");
        response404(client_sock, response);
        return 1;
    }

    // extensio mime_type: 得到content type
    get_extension(fullpath, extension);
    get_mime_type(extension, mime_type);

    // curr_time: 得到当前时间
    get_curr_time(curr_time, 256);

    // last_modified: 得到最后修改时间
    get_flmodified(fullpath, last_modified, 256);

    // content_length: 得到 content length
    content_length = get_file_len(fullpath);
    sprintf(content_len_str, "%zu", content_length);    // %zu用于输出size_t类型

    // 构造 response
    strcat(response, http_version);
    strcat(response, " 200 OK\r\n");

    strcat(response, "Server: ");
    strcat(response, server_name);
    strcat(response, "\r\n");

    strcat(response, "Date: ");
    strcat(response, curr_time);
    strcat(response, "\r\n");

    strcat(response, "Content-Length: ");
    strcat(response, content_len_str);
    strcat(response, "\r\n");

    strcat(response, "Content-type: ");
    strcat(response, mime_type);
    strcat(response, "\r\n");

    strcat(response, "Last-modified: ");
    strcat(response, last_modified);
    strcat(response, "\r\n");

    // connection_header_val: 根据request中的connect的类型 (close | keep-alive) 设置response的connection类型
    memset(connection_header_val, 0, sizeof(connection_header_val));
    get_header_value(request, "Connection", connection_header_val);
    if (!strcmp(connection_header_val, "close")){
        strcat(response, "Connection: close\r\n");
    }
    else{
        strcat(response, "Connection: keep-alive\r\n");
    }
    strcat(response, "\r\n");

    // printf("[response] response = \n%s\n", response);
    // printf("[response] fullpath = %s\n", fullpath);
    Send_nbytes(client_sock, response, strlen(response));
    return 0;
}

int response_post(int client_sock, Request* request){
    char response[4096];
    char content_length[32];

    memset(content_length, 0, sizeof(content_length));
    get_header_value(request, "Content-Length", content_length);
    if(strlen(content_length) == 0){
        strcat(response, http_version);
        strcat(response, " 401 Length Reqired\r\n");
        strcat(response, "\r\n");
        Send_nbytes(client_sock, response, strlen(response));
        return 1;
    }

    strcat(response, http_version);
    strcat(response, " 200 OK\r\n");
    strcat(response, "\r\n");

    Send_nbytes(client_sock, response, strlen(response));
    return 0;
}


// 请求错误
void response400(int client_sock){
    char response[1024];
    strcat(response, http_version);
    strcat(response, " 400 Bad request\r\n\r\n");
    // printf("[response] response = \n%s\n", response);
    Send_nbytes(client_sock, response, strlen(response));
}

// 没有请求的指定文件，返回404
void response404(int client_sock, char *response){
    strcat(response, http_version);
    strcat(response, " 404 Not Found\r\n\r\n");
    // printf("[response] response = \n%s\n", response);
    Send_nbytes(client_sock, response, strlen(response));
}

// 请求超时
void response408(int client_sock){
    return;
}

// 不支持的请求
void response501(int client_sock){
    char response[1024];
    strcat(response, http_version);
    strcat(response, " 501 Not Implemented\r\n\r\n");
    // printf("[response] response = \n%s\n", response);
    Send_nbytes(client_sock, response, strlen(response));
}

// 不支持的HTTP版本
void response505(int client_sock){
    char response[1024];
    strcat(response, http_version);
    strcat(response, " 505 HTTP Version not supported\r\n\r\n");
    // strcat(response, "Connection: close\r\n");
    // printf("[response] response = \n%s\n", response);
    Send_nbytes(client_sock, response, strlen(response));
}


// 检查是否是目录 y1 n0
int is_dir(const char *path){
    size_t len = strlen(path);
    if (path[len - 1] == '/'){
        return 1;
    }
    return 0;
}

// 返回文件扩展名
void get_extension(const char *path, char *result){
    size_t len = strlen(path);
    for (int i=len-1; i>=0; i--){
        int curr_len = len - i;
        if (path[i] == '.'){
            strncpy(result, path + (len - curr_len) + 1, curr_len - 1);
            return;
        }
    }
    strncpy(result, "none", 4);
}

// 根据扩展名确定MIME类型
void get_mime_type(const char *mime, char *type){
    if (!strcmp(mime, "html")){
        strcpy(type, "text/html");
    }
    else if (!strcmp(mime, "css")){
        strcpy(type, "text/css");
    }
    else if (!strcmp(mime, "png")){
        strcpy(type, "image/png");
    }
    else if (!strcmp(mime, "jpeg")){
        strcpy(type, "image/jpeg");
    }
    else if (!strcmp(mime, "gif")){
        strcpy(type, "image/gif");
    }
    else{
        strcpy(type, "application/octet-stream");
    }
}

// 获得当前时间
void get_curr_time(char *time_buf, size_t buf_size) {
  time_t raw_time;
  struct tm * timeinfo;

  time(&raw_time);
  timeinfo = localtime(&raw_time);
  strftime(time_buf, buf_size, "%a, %d %b %Y %H:%M:%S %Z", timeinfo);
}

// 获得文件最后修改时间
void get_flmodified(const char*path, char *last_mod_time, size_t buf_size) {
  struct stat st;
  struct tm *curr_gmt_time = NULL;
  stat(path, &st);
  curr_gmt_time = gmtime(&st.st_mtime);
  strftime(last_mod_time, buf_size, "%a, %d %b %Y %H:%M:%S %Z", curr_gmt_time);
}

// 返回文件长度
size_t get_file_len(const char* fullpath) {
  struct stat st;
  stat(fullpath, &st);  // stat函数用来获取指定路径的文件或者文件夹的信息
  return st.st_size;
}

// 根据给定键，设置值 如果不存在就不设置hvalue
void get_header_value(Request *request, const char *hname, char *hvalue){
    for (int i = 0; i < request->header_count; i++){
        if (!strcmp(request->headers[i].header_name, hname)){
            strcpy(hvalue, request->headers[i].header_value);
            return;
        }
    }
}

// 发送n字节
int send_nbytes(int sock, const void *p, int nbytes){
    size_t left_bytes = nbytes;       // 未发送
    ssize_t sent_bytes = 0;   // 已发送
    const char *ptr = p;

    while(left_bytes > 0){    // 还有未发送完的 继续发送
        if ((sent_bytes = send(sock, ptr + sent_bytes, nbytes, 0)) < 0){
            if(sent_bytes<0 && errno==EINTR){
                sent_bytes = 0;
            }
            else{
                return -1;
            }
        }
        left_bytes -= sent_bytes;
        ptr += sent_bytes;
    }
    return nbytes;
}

// 检查发送n字节是否成功
void Send_nbytes(int sock, const void *ptr, int nbytes){
    if (send_nbytes(sock, ptr, nbytes) != nbytes)
        printf("send_bytes error\n");
}

// 发送客户请求的文件
int send_file_to_client(int client_sock, char* full_path){
    int fd = open(full_path, O_RDONLY);
    if(fd<0){
        // dump_log("[IO][ERROR] Error when opening file %s for reading", full_path);
        return -1;
    }
    size_t file_length = get_file_len(full_path);
    ssize_t ret = sendfile(client_sock, fd, NULL, file_length);
    if (ret == -1){
        printf("sendfile error\n");
    }
    return 0;
}